﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Objects.SqlClient;
using System.Data.Objects;
using AexoManager.Datos;

namespace AexoManager.Datos
{
    public class AexoDataAccess
    {
        //private BDAexoManagerEntities db = new BDAexoManagerEntities();

        private BDAexoManagerEntitiesDespiece dbDespiece = new BDAexoManagerEntitiesDespiece();

        public AexoDataAccess()
        {
            //MappingConfigure.Inicializar();
            this.dbDespiece.Configuration.LazyLoadingEnabled = false;
        }

        #region General
        public void Commit()
        {
            dbDespiece.SaveChanges();
        }
        #endregion

        #region Proceso
        public List<Proceso> ProcesoObtenerLista()
        {
            return dbDespiece.Proceso.ToList<Proceso>();
        }

        public Proceso ProcesoGet(int idProceo)
        {

            var unProceso = dbDespiece.Proceso.First(p => p.IdProceso == idProceo);
            return unProceso;
        }

        public void ProcesoSave(Proceso unProceso)
        {
            dbDespiece.Proceso.Add(unProceso);
        }

        public void ProcesoUpdate(Proceso unProceso, string codigo, string descripcion, int idMaquina)
        {
            unProceso.Codigo = codigo;
            unProceso.Descripcion = descripcion;
            unProceso.IdMaquina = idMaquina;
        }

        public void ProcesoDelete(int idProceso)
        {
            Proceso unaProceso = dbDespiece.Proceso.First(p => p.IdProceso == idProceso);
            dbDespiece.Proceso.Remove(unaProceso);
        }

        #endregion

        #region Empleado

        public List<Empleado> EmpleadoObtenerLista()
        {
            return dbDespiece.Empleado.ToList<Empleado>();
        }

        public Empleado EmpleadoGet(int id)
        {

            var unEmpleado = dbDespiece.Empleado.First(p => p.IdEmpleado == id);
            return unEmpleado;
        }

        public void EmpleadoSave(Empleado unEmpleado)
        {
            dbDespiece.Empleado.Add(unEmpleado);
        }


        public void EmpleadoUpdate(Empleado unEmpleado, string nombre, string apellido, string alias)
        {
            unEmpleado.Nombre = nombre;
            unEmpleado.Apellido = apellido;
            unEmpleado.Alias = alias;
        }

        public void EmpleadoDelete(int idEmpleado)
        {
            Empleado unEmpleado = dbDespiece.Empleado.First(p => p.IdEmpleado == idEmpleado);
            dbDespiece.Empleado.Remove(unEmpleado);
        }
        #endregion

        #region Parte
        public Parte ParteGet(string codigoParte)
        {

            var unaParte = dbDespiece.Parte.First(p => p.Codigo == codigoParte);
            return unaParte;
        }
        public Parte ParteGet(string codigoParte, params string[] propsACargar)
        {

            System.Data.Entity.Infrastructure.DbQuery<Parte> query = null;
            query = dbDespiece.Parte;
            foreach (var item in propsACargar)
            {
                query = query.Include(item);

            }
            var unaParte = query.First(p => p.Codigo == codigoParte);

            return unaParte;
        }

        public Parte ParteGet(int id)
        {

            var unaParte = dbDespiece.Parte.First(p => p.IdParte == id);
            return unaParte;
        }
        public Parte ParteGet(int id, params string[] propsACargar)
        {
            System.Data.Entity.Infrastructure.DbQuery<Parte> query = null;
            query = dbDespiece.Parte;
            foreach (var item in propsACargar)
            {
                query = query.Include(item);    
                
            }
            var unaParte = query.First(p => p.IdParte == id);

            return unaParte;
        }

        public List<Parte> ParteObtenerLista()
        {
            return dbDespiece.Parte.Take(1000).ToList<Parte>();
        }

        public List<Parte> ParteObtenerLista(string descripcion, int nroRegistro, int cantidadRegistros)
        {
            return dbDespiece.Parte.Where(x => SqlFunctions.PatIndex(descripcion + "%", x.Descripcion) > 0)
                .OrderBy(x => x.Descripcion)
                .Skip(nroRegistro)
                .Take(cantidadRegistros)
                .ToList<Parte>();
        }

        public int ParteObtenerCount(string descripcion)
        {
            return dbDespiece.Parte.Where(x => SqlFunctions.PatIndex(descripcion + "%", x.Descripcion) > 0).Count();
        }

        public List<Parte> PartesFinalesObtenerLista()
        {
            return dbDespiece.Parte.Where(x => x.EsParteFinal == true).ToList();
        }


        /// <summary>
        /// Crea una parte nueva.
        /// </summary>
        /// <param name="unaParte"></param>
        public void ParteSave(Parte unaParte)
        {
            dbDespiece.Parte.Add(unaParte);
        }

        public void ParteUpdate(Parte unaParte, string codigo, string descripcion)
        {
            unaParte.Codigo = codigo;
            unaParte.Descripcion = descripcion;
        }

        public void ParteDelete(int idParte)
        {
            Parte unaParte = dbDespiece.Parte.First(p => p.IdParte == idParte);
            dbDespiece.Parte.Remove(unaParte);
        }
        #endregion

        #region CostoParte
        public CostoParte CostoParteGet(int idParte)
        {

            try
            {
                var unCosto = dbDespiece.CostoParte.SingleOrDefault(x => x.IdParte == idParte
                                                                            && x.FechaHasta >= DateTime.Now
                                                                            && x.FechaDesde <= DateTime.Now);
                return unCosto;
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        public IList<CostoParte> CostosPartesGet(int idParte)
        {
            try
            {
                var costos = dbDespiece.CostoParte.Where(x => x.IdParte == idParte).ToList<CostoParte>();
                return costos;
            }
            catch (Exception)
            {

                throw;
            }

        }

        public void CostoPartesAgregar(CostoParte unCosto)
        {
            try
            {
                dbDespiece.CostoParte.Add(unCosto);
            }
            catch (Exception)
            {
                
                throw;
            }
        }
        #endregion

        #region RegistroProceso

        public RegistroProceso RegistroProcesoObtener(int idRegistroProceso)
        {
            return dbDespiece.RegistroProceso.First(p => p.IdRegistroProceso == idRegistroProceso);
        }
        /// <summary>
        /// Obtiene una lista Iqueryable de RegistroProceso donde el registro tenga fecha de fin, pero no este finalizado
        /// </summary>
        /// <param name="idParte"></param>
        /// <param name="fin"></param>
        /// <param name="finalizo"></param>
        /// <param name="idUsuario"></param>
        /// <param name="idProceso"></param>
        /// <returns></returns>
        public List<RegistroProceso> RegistroProcesoObtenerNoFinalizado(int? idParte, int? idEmpleado, int? idProceso)
        {
            var listaNoFinalizados = dbDespiece.RegistroProceso.Where(m => (m.IdParte == idParte || idParte == null)
                                                                && m.Fin != null
                                                                && m.Finalizo == false
                                                                && (m.IdEmpleado == idEmpleado || idEmpleado == null)
                                                                && (m.IdProceso == idProceso || idProceso == null)).ToList();

            var lamda = listaNoFinalizados
                           .GroupBy(x => new { x.IdComienzo, x.Parte, x.Proceso, x.Cantidad, x.Empleado })
                           .Select(z => new RegistroProceso
                               {
                                   IdComienzo = z.Key.IdComienzo,
                                   Parte = z.Key.Parte,
                                   Proceso = z.Key.Proceso,
                                   Cantidad = z.Key.Cantidad,
                                   Empleado = z.Key.Empleado,
                                   Inicio = z.Max(x => x.Inicio),
                                   IdRegistroProceso = z.Max(x => x.IdRegistroProceso)
                               });
            /*
            var query = from e in listaNoFinalizados
                        group e by new { e.IdComienzo, e.Parte, e.Proceso, e.Cantidad, e.Usuario } into g
                        select new RegistroProceso
                        {
                            IdComienzo = g.Key.IdComienzo,
                            Parte = g.Key.Parte,
                            Proceso = g.Key.Proceso,
                            Cantidad = g.Key.Cantidad,
                            Usuario = g.Key.Usuario,
                            Inicio = (from e in listaNoFinalizados
                                      where e.IdComienzo == g.Key.IdComienzo
                                      select e.Inicio).Max(),
                            IdRegistroProceso = (from e in listaNoFinalizados
                                                 where e.IdComienzo == g.Key.IdComienzo
                                                 select e.IdRegistroProceso).Max(),
                        };
            */
            return lamda.ToList();

        }

        /// <summary>
        /// Obtiene una lista Iqueryable de RegistroProceso que esten iniciado pero no esten finalizado. 
        /// </summary>
        /// <param name="idParte"></param>
        /// <param name="idUsuario"></param>
        /// <returns></returns>
        public List<RegistroProceso> RegistroProcesoObtenerIniciados(int idParte, int idProceso, int idEmpleado)
        {
            try
            {
                return dbDespiece.RegistroProceso.Where(p => p.IdParte == idParte
                                                        && p.Inicio != null
                                                        && p.Fin == null
                                                        && p.IdProceso == idProceso
                                                        && p.IdEmpleado == idEmpleado).ToList();
            }
            catch (Exception)
            {

                throw;
            }
        }

        public List<RegistroProceso> RegistroProcesoObtenerIniciados()
        {
            return dbDespiece.RegistroProceso.Where(p => p.Inicio != null
                                                            && p.Fin == null).ToList();
        }

        /// <summary>
        /// Obtiene una lista Iqueryable de los RegistrosProceso que finalizaron (Finalizo = True)
        /// </summary>
        /// <returns></returns>
        public List<RegistroProceso> RegistroProcesoObtenerFinalizados()
        {
            return dbDespiece.RegistroProceso.Where(m => m.Finalizo == true).ToList();
        }

        /// <summary>
        /// Obtiene el Maximo IdComienzo utilizado en la tabla de RegistroProceso
        /// </summary>
        /// <returns></returns>
        public int RegisroProcesoMaxIdComienzo()
        {
            var maximo = dbDespiece.RegistroProceso.Max(m => m.IdComienzo);
            return (maximo != null) ? maximo.Value : 0;
        }

        /// <summary>
        /// Trae un RegistroProceso que no tenga fecha de fin a partir del Codigo de parte
        /// </summary>
        /// <param name="codigoParte"></param>
        /// <returns></returns>
        public RegistroProceso RegistroProcesoGetIniciado(string codigoParte)
        {
            return dbDespiece.RegistroProceso.First(i => i.Parte.Codigo == codigoParte && i.Fin == null);
        }

        /// <summary>
        /// Crea un nuevo RegistroProceso
        /// </summary>
        /// <param name="unaParte">Objeto Parte relacionado a este nuevo RegistroProceso</param>
        /// <param name="idProceso">Id del proceso asociado</param>
        /// <param name="cantidad">Cantidad de partes que se estaran procesando</param>
        /// <param name="idUsuario">Usuario que realiza esta tarea</param>
        /// <param name="idComienzo">Id comienzo relacionado</param>
        /// <param name="inicio">Fecha de inicio</param>
        public void RegistroProcesoSave(RegistroProceso unRegistro)
        {
            dbDespiece.RegistroProceso.Add(unRegistro);
        }

        /// <summary>
        /// Actualiza la fecha fin y el flag de Finalizo de un RegistroProceso
        /// </summary>
        /// <param name="unRegistro"></param>
        /// <param name="fin"></param>
        /// <param name="finalizo"></param>
        public void RegistroProcesoUpdate(RegistroProceso unRegistro, DateTime fin, bool finalizo)
        {
            unRegistro.Fin = fin;
            unRegistro.Finalizo = finalizo;
        }

        #endregion

        #region Despiece
        /// <summary>
        /// Obtiene el Arbol de despiece del producto y parte que se envia por parametro.
        /// </summary>
        /// <param name="unProducto"></param>
        /// <param name="unaParte"></param>
        /// <returns></returns>
        public List<ISPDespiece> DespieceConsultar(Producto unProducto)
        {
            List<ISPDespiece> resultado;

            try
            {
                resultado = dbDespiece.sp_DespieceHijosConsultar(unProducto.IdProducto).ToList<ISPDespiece>();
            }
            catch (Exception)
            {

                throw;
            }
            return resultado;
        }

        public List<ISPDespiece> DespieceConsultar(Producto unProducto, Parte unaParte)
        {
            List<ISPDespiece> resultado;

            try
            {
                resultado = dbDespiece.usp_DespieceHijosPartesObtener(unProducto.IdProducto, unaParte.IdParte).ToList<ISPDespiece>();
            }
            catch (Exception)
            {

                throw;
            }
            return resultado;
        }

        public IList<Despiece> DespieceConsultarHijos(Producto unProducto, Parte unaParte)
        {
            IList<Despiece> despieces = new List<Despiece>();
            try
            {
                var resultado = dbDespiece.usp_DespieceObtenerHijos(unProducto.IdProducto, unaParte.IdParte);
                foreach (var item in resultado)
                {
                    Parte unaParteDesc = ParteGet(item.IdParte);
                    Despiece unDespiece = new Despiece();
                    unDespiece.Parte = new Parte { IdParte = item.IdParte, Descripcion = unaParteDesc.Descripcion, Codigo = unaParteDesc.Codigo };
                    unDespiece.Producto = new Producto { IdProducto = item.IdProducto };
                    unDespiece.Cantidad = (int)item.Cantidad;

                    despieces.Add(unDespiece);

                    //partes.Add(new Parte { IdParte = item.IdParte, Codigo = item.Codigo, Descripcion = item.Descripcion });
                }
            }
            catch (Exception)
            {
                throw;
            }

            return despieces;
        }

        public void DespieceAgregarHijo(Despiece unDespiece, Parte partePadre)
        {
            try
            {
                var idProducto = unDespiece.Producto.IdProducto;
                dbDespiece.usp_DespieceAgregarHijo(idProducto, partePadre.IdParte, idProducto, unDespiece.Parte.IdParte, unDespiece.Cantidad);

            }
            catch (Exception)
            {
                throw;
            }
        }

        public void DespieceAgregarRoot(Despiece unDespiece)
        {
            try
            {
                dbDespiece.usp_DespieceAgregarRoot(unDespiece.Producto.IdProducto, unDespiece.Parte.IdParte, unDespiece.Cantidad);

            }
            catch (Exception)
            {

                throw;
            }
        }
        #endregion

        #region Insumo
        public void InsumoAgregar(Insumo unInsumo, Parte partePadre)
        {
            try
            {
                dbDespiece.usp_InsumoAgregarHijo(partePadre.IdParte, unInsumo.Parte.IdParte, unInsumo.Cantidad);

            }
            catch (Exception)
            {
                throw;
            }
        }

        public Insumo InsumoObtener(Insumo unInsumo)
        {
            try
            {
                return dbDespiece.Insumo.SingleOrDefault(x => x.IdParte == unInsumo.IdParte && x.Nivel == 1);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<ISPInsumo> InsumoObtenerDespiece(Parte unaParte)
        {
            try
            {
                return dbDespiece.usp_InsumoHijosConsultar(unaParte.IdParte).ToList<ISPInsumo>();
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region Producto
        public Producto ProductoGet(int id)
        {

            var unProducto = dbDespiece.Producto.First(p => p.IdProducto == id);
            return unProducto;
        }

        public Producto ProductoGet(string codigoProducto)
        {
            var unProducto = dbDespiece.Producto.First(p => p.Codigo == codigoProducto);
            return unProducto;
        }
        public IList<Producto> ProductoObtenerLista()
        {
            return dbDespiece.Producto.ToList<Producto>();
        }

        /// <summary>
        /// Guarda un Producto
        /// </summary>
        /// <param name="unProducto"></param>
        public void ProductoSave(Producto unProducto)
        {
            var value = dbDespiece.Producto.Add(unProducto);
            dbDespiece.SaveChanges();
        }
        public void ProductoUpdate(Producto unProducto, string codigo, string descripcion, int idParteFinal)
        {
            unProducto.Codigo = codigo;
            unProducto.Descripcion = descripcion;
            unProducto.Parte = new Parte { IdParte = idParteFinal };
        }
        #endregion
    }
}
